class Game2048 {
    constructor() {
        this.size = 4;
        this.grid = [];
        this.score = 0;
        this.won = false;
        this.over = false;
        
        this.tileContainer = document.getElementById('tile-container');
        this.scoreContainer = document.getElementById('score');
        this.messageContainer = document.getElementById('game-message');
        
        this.startTouchX = 0;
        this.startTouchY = 0;
        
        this.setup();
        this.updateDisplay();
    }
    
    setup() {
        this.grid = Array(this.size).fill().map(() => Array(this.size).fill(0));
        this.score = 0;
        this.won = false;
        this.over = false;
        this.clearMessage();
        
        this.addRandomTile();
        this.addRandomTile();
    }
    
    restart() {
        this.clearTileContainer();
        this.setup();
        this.updateDisplay();
    }
    
    clearTileContainer() {
        while (this.tileContainer.firstChild) {
            this.tileContainer.removeChild(this.tileContainer.firstChild);
        }
    }
    
    addRandomTile() {
        const emptyCells = [];
        for (let x = 0; x < this.size; x++) {
            for (let y = 0; y < this.size; y++) {
                if (this.grid[x][y] === 0) {
                    emptyCells.push({ x, y });
                }
            }
        }
        
        if (emptyCells.length > 0) {
            const cell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
            this.grid[cell.x][cell.y] = Math.random() < 0.9 ? 2 : 4;
        }
    }
    
    move(direction) {
        if (this.isGameTerminated()) return;
        
        let moved = false;
        const vector = this.getVector(direction);
        const traversals = this.buildTraversals(vector);
        
        const currentState = this.serialize();
        
        this.prepareTiles();
        
        traversals.x.forEach(x => {
            traversals.y.forEach(y => {
                const cell = { x, y };
                const tile = this.grid[x][y];
                
                if (tile !== 0) {
                    const positions = this.findFarthestPosition(cell, vector);
                    const next = positions.next;
                    
                    if (next && this.grid[next.x][next.y] === tile && !this.grid[next.x][next.y].mergedFrom) {
                        const merged = tile * 2;
                        this.grid[next.x][next.y] = merged;
                        this.grid[x][y] = 0;
                        
                        this.score += merged;
                        
                        if (merged === 2048 && !this.won) this.won = true;
                        
                        this.grid[next.x][next.y].mergedFrom = true;
                    } else {
                        this.grid[positions.farthest.x][positions.farthest.y] = tile;
                        if (positions.farthest.x !== x || positions.farthest.y !== y) {
                            this.grid[x][y] = 0;
                        }
                    }
                    
                    if (!this.positionsEqual(cell, { x: positions.farthest.x, y: positions.farthest.y }) ||
                        (next && this.grid[next.x][next.y] === tile * 2)) {
                        moved = true;
                    }
                }
            });
        });
        
        if (moved) {
            this.addRandomTile();
            
            if (!this.movesAvailable()) {
                this.over = true;
            }
            
            this.updateDisplay();
        }
    }
    
    getVector(direction) {
        const map = {
            0: { x: 0,  y: -1 }, // 上
            1: { x: 1,  y: 0 },  // 右
            2: { x: 0,  y: 1 },  // 下
            3: { x: -1, y: 0 }   // 左
        };
        return map[direction];
    }
    
    buildTraversals(vector) {
        const traversals = { x: [], y: [] };
        
        for (let pos = 0; pos < this.size; pos++) {
            traversals.x.push(pos);
            traversals.y.push(pos);
        }
        
        if (vector.x === 1) traversals.x = traversals.x.reverse();
        if (vector.y === 1) traversals.y = traversals.y.reverse();
        
        return traversals;
    }
    
    findFarthestPosition(cell, vector) {
        let previous;
        
        do {
            previous = cell;
            cell = { x: previous.x + vector.x, y: previous.y + vector.y };
        } while (this.withinBounds(cell) && this.cellAvailable(cell));
        
        return {
            farthest: previous,
            next: cell
        };
    }
    
    withinBounds(position) {
        return position.x >= 0 && position.x < this.size &&
               position.y >= 0 && position.y < this.size;
    }
    
    cellAvailable(cell) {
        return this.withinBounds(cell) && this.grid[cell.x][cell.y] === 0;
    }
    
    prepareTiles() {
        for (let x = 0; x < this.size; x++) {
            for (let y = 0; y < this.size; y++) {
                if (this.grid[x][y] !== 0) {
                    this.grid[x][y].mergedFrom = false;
                }
            }
        }
    }
    
    positionsEqual(first, second) {
        return first.x === second.x && first.y === second.y;
    }
    
    movesAvailable() {
        return this.cellsAvailable() || this.tileMatchesAvailable();
    }
    
    cellsAvailable() {
        return !!this.availableCells().length;
    }
    
    availableCells() {
        const cells = [];
        for (let x = 0; x < this.size; x++) {
            for (let y = 0; y < this.size; y++) {
                if (this.grid[x][y] === 0) {
                    cells.push({ x, y });
                }
            }
        }
        return cells;
    }
    
    tileMatchesAvailable() {
        for (let x = 0; x < this.size; x++) {
            for (let y = 0; y < this.size; y++) {
                const tile = this.grid[x][y];
                if (tile !== 0) {
                    for (let direction = 0; direction < 4; direction++) {
                        const vector = this.getVector(direction);
                        const cell = { x: x + vector.x, y: y + vector.y };
                        
                        const other = this.grid[cell.x] && this.grid[cell.x][cell.y];
                        if (other && other === tile) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    serialize() {
        return {
            grid: this.grid.map(column => column.slice()),
            score: this.score,
            over: this.over,
            won: this.won
        };
    }
    
    updateDisplay() {
        this.clearTileContainer();
        
        for (let x = 0; x < this.size; x++) {
            for (let y = 0; y < this.size; y++) {
                if (this.grid[x][y] !== 0) {
                    this.addTile(x, y, this.grid[x][y]);
                }
            }
        }
        
        this.scoreContainer.textContent = this.score;
        
        if (this.won && !this.keepPlaying) {
            this.message(true);
        } else if (this.over) {
            this.message(false);
        }
    }
    
    addTile(x, y, value) {
        const wrapper = document.createElement('div');
        const inner = document.createElement('div');
        
        const position = this.normalizePosition({ x, y });
        
        wrapper.setAttribute('class', 'tile tile-' + value);
        wrapper.setAttribute('data-value', value);
        wrapper.style.left = position.x + 'px';
        wrapper.style.top = position.y + 'px';
        
        inner.textContent = value;
        wrapper.appendChild(inner);
        
        this.tileContainer.appendChild(wrapper);
    }
    
    normalizePosition(position) {
        return {
            x: position.y * 25 + '%',
            y: position.x * 25 + '%'
        };
    }
    
    message(won) {
        const type = won ? 'game-won' : 'game-over';
        const messageText = won ? '你赢了！' : '游戏结束！';
        
        this.messageContainer.setAttribute('class', 'game-message ' + type);
        this.messageContainer.querySelector('p').textContent = messageText;
        this.messageContainer.style.display = 'block';
    }
    
    clearMessage() {
        this.messageContainer.setAttribute('class', 'game-message');
        this.messageContainer.style.display = 'none';
    }
    
    isGameTerminated() {
        return this.over || (this.won && !this.keepPlaying);
    }
    
    // 键盘事件处理
    handleKeyDown(event) {
        const modifiers = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
        const mapped = {
            38: 0, // 上箭头
            39: 1, // 右箭头
            40: 2, // 下箭头
            37: 3  // 左箭头
        };
        
        if (!modifiers && mapped[event.which] !== undefined) {
            event.preventDefault();
            this.move(mapped[event.which]);
        }
    }
    
    // 触摸事件处理
    handleTouchStart(event) {
        if (event.touches.length > 1) return;
        
        if (event.touches.length === 1) {
            this.startTouchX = event.touches[0].clientX;
            this.startTouchY = event.touches[0].clientY;
        }
        
        event.preventDefault();
    }
    
    handleTouchMove(event) {
        event.preventDefault();
    }
    
    handleTouchEnd(event) {
        if (event.changedTouches.length > 1) return;
        
        const endTouchX = event.changedTouches[0].clientX;
        const endTouchY = event.changedTouches[0].clientY;
        
        const dx = endTouchX - this.startTouchX;
        const dy = endTouchY - this.startTouchY;
        
        const absDx = Math.abs(dx);
        const absDy = Math.abs(dy);
        
        if (Math.max(absDx, absDy) > 10) {
            if (absDx > absDy) {
                this.move(dx > 0 ? 1 : 3); // 右 : 左
            } else {
                this.move(dy > 0 ? 2 : 0); // 下 : 上
            }
        }
        
        event.preventDefault();
    }
}

// 初始化游戏
let game;

document.addEventListener('DOMContentLoaded', () => {
    game = new Game2048();
    
    // 键盘事件
    document.addEventListener('keydown', (event) => {
        game.handleKeyDown(event);
    });
    
    // 触摸事件
    const gameContainer = document.querySelector('.game-container');
    gameContainer.addEventListener('touchstart', (event) => {
        game.handleTouchStart(event);
    });
    gameContainer.addEventListener('touchmove', (event) => {
        game.handleTouchMove(event);
    });
    gameContainer.addEventListener('touchend', (event) => {
        game.handleTouchEnd(event);
    });
});
